#ifndef MESSAGE_NETWORK_H_INCLUDED
#define MESSAGE_NETWORK_H_INCLUDED

/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "message_logic.h"

namespace engine
{
    /**
     * net_header is a POD type of 32 bits length that is necessary for all outward-bound messages
     * it describes where the message will go, what should be done upon receipt, and sequence for
     * reordering
     */
    struct net_header
    {
    public:
        /** enum for packet processing techniques
        NORMAL - standard update packet, not ACKed, and old packets are discarded
        ORDERED - ensured update packet, ACKed, and rebroadcast until ACK received. Old packets discarded
        FORCE - not ACKed, but always processed and never discarded
        ACK - Packet acknowledgement
        NACK - Negative packet acknowledgement
        RESET - Reset count (and probably some other stuff to be determined later)
        CONNECTION_REQ - First packet sent from a remote client, asks server for a connection slot
        CONNECTION_ACPT - Server acknowledges connection to client and sends hostid
        CONNECTION_RJCT - Server denies connection to client
        CONNECTION_TERM - Connection is terminated (initiated by either server or client)
        QUERY - A Packet (typically broadcast) from a host looking to find open servers on the network
        REPLY - A packet reply to a query indicating that the host is available
        */
        enum {NORMAL, ORDERED, FORCE, ACK, NACK, RESET, SYN, CONNECTION_REQ, CONNECTION_ACPT,
              CONNECTION_RJCT, CONNECTION_TERM, QUERY, REPLY};

        net_header() : dest_host(0), packet_type(NORMAL) {}
        net_header(hostid d, uint16_t s) : dest_host(d), packet_type(NORMAL), seq(s) {}

        hostid dest_host; //Host id of where this packet is supposed to go to
        uint8_t packet_type; //How the host should interpret (Most are NORMAL and are just directly processed
                             //Packets labelled "ORDERED" need to be ACKed upon receipt, and will be rebroadcast
                             //until that ACK is processed by the source host.
        uint16_t seq; //Sequence number. Each subsequent packet sent gets an incremented count
    };

    /*Inter-Network message is a conglomeration of all the existing messages (i.e. any cross-thread messages can
      also be a network message) so we can use the mpi to send messages to threads on different hosts too!*/
    struct inter_network_msg
    {
    public:
        inter_network_msg() : header() {} //UNSAFE!
        inter_network_msg(const message_header& m) : header() {memcpy(payload, &m, m.get_size());}
        inter_network_msg(const net_header& n, const message_header& m) : header(n) {memcpy(payload, &m, m.get_size());}

        const message_header& cast() const {return *(reinterpret_cast<const message_header*>(payload));}

        net_header header;
        char payload [sizeof(Message_Logic)];
    };

    static const task_id network_thread_tid = get_task_id_by_name("network_thread");

    struct disconnect_network_msg
    {
    public:
        disconnect_network_msg() : header(0,0), m(network_thread_tid, NETWORK_MSG, 0)
        {header.packet_type = header.CONNECTION_TERM;}

        net_header header;
        message_header m;
    };

    /*Local-Network message is a message that the MPI sends to tell the network thread how to work (i.e. connect,
      disconnect, ping, etc
    */
    struct local_network_msg
    {
        local_network_msg(): header(network_thread_tid, NETWORK_MSG, 0, sizeof(local_network_msg)) {}
        local_network_msg(const message_header& m) {memcpy(this, &m, m.get_size());}

        message_header header;

        enum {CONNECT, CONNECT_IPV6, CONNECT_IPV4, DISCONNECT, KICK, PING};

        uint8_t command;

        union
        {
            uint64_t num;
            char str [64];
            const char* strp;
        } arg;
    };
}

#endif // MESSAGE_NETWORK_H_INCLUDED
